home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
NOVA - For the NeXT Workstation
/
NOVA - For the NeXT Workstation.iso
/
SourceCode
/
Registration
/
Registration.m
< prev
next >
Wrap
Text File
|
1992-12-19
|
22KB
|
922 lines
/*
* (C) 1992 Simson Garfinkel and Associates, Inc.
*
* NeXTSTEP developers may freely use and redistribute this software as long
* as credit is given to Simson Garfinkel and Associates.
*
* EXPORT RESTRICTIONS:
*
* You may not ship the source-code module des.c outside of the US or canada.
*
* You may ship a program which uses the des.o compiled module outside of the
* United States to any type T or type V country as long as you do not provide
* cryptographic services to the user in your program and you clearly
* declare "commodity control number 5D11A" on your export declaration.
*
* Type T countries include all countries in the Western Hemisphere except Cuba.
* Type V countries include all countries in the Eastern Hemisphere except
* the previous communist block countries and the People's Republic of China,
* Vietnam, Cambodia, and Laos.
*
* For further information, contact the Office of Export Control:
*
* Bureau of Export Administration
* P.O. Box 273
* Washington, DC 20044
* 202-377-2694
*/
#import <appkit/appkit.h>
#import "Registration.h"
#import <netdb.h>
#import <pwd.h>
#import <NXCType.h>
/* Network Registration Stuff
*
* "embedded" is our embedded registration string, for stamped binaries.
* It contains our current network token.
* Is is decrypted when we are running.
*/
struct registration_string embedded =
{
SERIAL_GUARD_1,SERIAL_GUARD_2,
0, /* ctime */
0, /* mtime */
0, /* checksum */
0,0,0,0,0,0,0,0,0,0, /* ls */
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0, /* name */
SERIAL_GUARD_1,SERIAL_GUARD_2
};
#define REG "SingleUserRegistrationString"
#define ANNOUNCEMENT_FREQUENCY 60.0 * 10 /* how often to announce ourselves */
/* Static variables */
static port_name_t myMachPort;
static int send_sock; /* the talking socket */
static int recv_sock; /* receiving socket */
static int recv_fd; /* receiving fd */
static mutex_t randomt; /* mutex for random number genreator */
static long otherHosts[1024]; /* other hosts running with our license */
static int numOtherHosts;
static DPSTimedEntry announcementTE;
static DPSTimedEntry expiredTE;
static int licensed; /* are we licensed? */
static id registrationPanel;
static id licensePanel;
static long start_time;
static char username[10];
static char person[64];
static long session_number;
/* for probing on licensing */
static int probes_found;
static unsigned char probing_num[3]; /* number we are probing */
static char probed_person[9];
static char probed_username[64];
static char probed_host[64];
#import "RegSupport.m"
/****************************************************************
MACH MESSAGE FUNCTIONS
****************************************************************/
/* structure for mach messaging */
typedef struct {
msg_header_t h;
msg_type_t t1;
int command;
msg_type_t t2;
const char *data;
} myMessage;
#define BROADCAST 0xffffffff
#define PING_EVERYBODY 1
#define PONG_EVERYBODY 2
/* Send a mach message to the main thread */
void message_main_thread(int command,const char *data)
{
myMessage msg;
memset(&msg,0,sizeof(msg)); /* zero my message */
msg.h.msg_simple = TRUE;
msg.h.msg_size = sizeof(msg);
msg.h.msg_type = MSG_TYPE_NORMAL; /* normal message */
msg.h.msg_local_port = 0;
msg.h.msg_remote_port = myMachPort;
/* fill in the type descriptor */
msg.t1.msg_type_name = MSG_TYPE_INTEGER_32;
msg.t1.msg_type_size = 32;
msg.t1.msg_type_number = 1;
msg.t1.msg_type_inline = YES;
msg.t1.msg_type_longform = FALSE;
msg.t1.msg_type_deallocate = FALSE;
/* fill in the type descriptor */
msg.t2.msg_type_name = MSG_TYPE_STRING_C;
msg.t2.msg_type_size = 32;
msg.t2.msg_type_number = 1;
msg.t2.msg_type_inline = FALSE;
msg.t2.msg_type_longform = FALSE;
msg.t2.msg_type_deallocate = FALSE;
msg.command = command;
msg.data = data;
msg_send(&msg.h,SEND_NOTIFY,0); /* send and don't wait */
}
/* This gets called in the main thread from message_main_thread
*/
DPSPortProc portListener(msg_header_t *msgp,void *userData)
{
myMessage *msg = (myMessage *)msgp;
char *data;
data = alloca(strlen(msg->data)+1);
strcpy(data,msg->data);
switch(msg->command){
case C_PRINT:
NXRunAlertPanel(0,data,0,0,0);
return 0;
case C_DUPLICATE_SINGLE_USER:
NXRunAlertPanel("License Violation",
"%s is already using your single user license string",
"Abort program",0,0,data);
[NXApp fatalError];
return 0;
case C_TOO_MANY_NETWORK:
NXRunAlertPanel("License Violation",
"More than %d copies of this program are already running on your network.",
"Abort program",0,0,lsNumUsers(&embedded.ls));
[NXApp fatalError];
return 0;
}
return 0;
}
/****************************************************************
UDP STUFF
****************************************************************/
/*
* udp_announcer:
* Announce my whereabouts to the world.
* Randomly pause between 1 and 10 seconds, which should be sufficient for most
* networks.
*/
static void udp_announcer(int address)
{
struct sockaddr_in sin;
struct regNetToken nt;
int pause;
long now;
long duration;
if(address!=PING_EVERYBODY){
mutex_lock(randomt);
pause = random() % RANDOM_SLEEP_TIME;
mutex_unlock(randomt);
sleep(pause);
}
time(&now);
duration = now - start_time;
memset(&nt,0,sizeof(nt));
nt.version = 0;
strcpy(nt.username,username);
strcpy(nt.person,person);
nt.ls = embedded.ls;
nt.session_number = session_number;
nt.time[0] = duration & 0xff;
nt.time[1] = (duration >> 8) & 0xff;
nt.time[2] = (duration >> 16) & 0xff;
nt.time[3] = (duration >> 24) & 0xff;
nt.command = C_PONG;
memset(&nt.userData,0,sizeof(nt.userData));
switch(address){
case PING_EVERYBODY:
address = BROADCAST;
nt.command = C_PING;
break;
case PONG_EVERYBODY:
address = BROADCAST;
nt.command = C_PONG;
break;
}
sin.sin_family = AF_INET;
sin.sin_port = PRODUCT_UDP_PORT; /* our port */
*(int *)&sin.sin_addr = address;
sendto(send_sock,&nt,sizeof(nt),0,(struct sockaddr *)&sin,sizeof(sin));
}
/* duplicate_ls():
*
* Called when somebody tells me they are using my accession number.
* 1. Ignore them if they were started after we were.
* 2. If they are using our accession number on a SINGLE USER license,
* 3. If they are using a multi-user number, then count how many
* people are left
*/
void duplicate_ls(int fromaddr,struct regNetToken *nt)
{
long now;
long my_duration,their_duration;
char buf[1024];
char buf2[1024];
int i;
/* First tell them that something is amis */
time(&now);
my_duration = now - start_time;
their_duration = nt->time[3]<<24 | nt->time[2]<<16 | nt->time[1]<<8 | nt->time[0];
if(my_duration+REG_WINDOW > their_duration){
/* They haven't been running as long as we have been. */
if(embedded.ls.flag & REG_SINGLEUSER){
/* but they are using our single user string! */
sprintf(buf,"%s (%s) on %s is trying to run this application with your "
"single-user license string.",
nt->person,nt->username,hostName(fromaddr,buf2));
message_main_thread(C_PRINT,buf);
}
return; /* don't annoy our user any more */
}
if(embedded.ls.flag & REG_SINGLEUSER){
/* we came second on single-user string */
sprintf(buf,"%s (%s) on %s",
nt->person,
nt->username,
hostNumberToAscii(fromaddr,buf2));
message_main_thread(C_DUPLICATE_SINGLE_USER,buf);
}
for(i=0;i<numOtherHosts;i++){
if(otherHosts[i]==fromaddr) return; /* already got this host */
}
otherHosts[numOtherHosts++] = fromaddr;
/* More than maximum allowed hosts? */
if(numOtherHosts >= lsNumUsers(&embedded.ls)){
message_main_thread(C_TOO_MANY_NETWORK,"");
}
}
/* udp_listener():
*
* Listen for incoming UDP commands and execute them.
*/
static void udp_listener(void *userData)
{
struct sockaddr_in from;
int fromlen;
long fromaddr;
char received_udp[4096];
int cc;
while(1){
memset(&from,0,sizeof(from));
fromlen = sizeof(from);
cc = recvfrom(recv_sock, received_udp, sizeof(received_udp),
0, (struct sockaddr *)&from, &fromlen);
fromaddr=*(int *)&from.sin_addr;;
if(cc==sizeof(struct regNetToken)){
char hbuf[256];
struct regNetToken *nt = (struct regNetToken *)received_udp;
if(nt->session_number==session_number){
/* got a message from myself. Ignore */
continue;
}
/* Check for match with our license string */
if(!memcmp(nt->ls.num,embedded.ls.num,3)){
duplicate_ls(fromaddr,nt);
}
/* decode command */
switch(nt->command){
case C_PING:
cthread_fork((cthread_fn_t)udp_announcer,(any_t)fromaddr);
break;
case C_PRINT:
{
char *buf;
buf = alloca(1024+strlen(nt->userData));
sprintf(buf,"Message from %s on %s: %s",
nt->username,
hostNumberToAscii(fromaddr,hbuf),
nt->userData);
message_main_thread(C_PRINT,buf);
}
break;
case C_PONG:
/* Check for match with probe */
if(!memcmp(nt->ls.num,probing_num,3)){
strcpy(probed_person,nt->person);
strcpy(probed_username,nt->username);
strncpy(probed_host,hostName(fromaddr,hbuf),63);
probed_host[63] = 0;
probes_found++;
}
break;
}
}
}
}
/* called by announcement TE */
static void announcement_handler()
{
if(embedded.ls.flag & REG_DEMO){
/* don't announce if we are a demo copy */
return;
}
cthread_fork((cthread_fn_t)udp_announcer,(any_t)PONG_EVERYBODY);
}
/****************************************************************
INITIALIZATION
****************************************************************/
static void network_init()
{
struct sockaddr_in sin;
struct passwd *pw;
/* NETWORK INITIALIZATION */
memset(otherHosts,0,sizeof(otherHosts));
memset(probing_num,0,sizeof(probing_num));
numOtherHosts = 0;
/* set up connections for listening */
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = PRODUCT_UDP_PORT; /* our port */
recv_sock = socket(AF_INET,SOCK_DGRAM,0); /* get a socket */
if(recv_sock<0){
NXRunAlertPanel(0,"Could not create Internet socket.","Abort program",0,0);
[NXApp fatalError];
}
recv_fd = bind(recv_sock,(struct sockaddr *)&sin,sizeof(sin));
if(recv_fd<0){
NXRunAlertPanel(0,"Could not bind to network. You are probably "
"running another copy of %s on this computer.",
"Abort program",0,0,[NXApp appName]);
[NXApp fatalError];
}
randomt = mutex_alloc();
srandom(time(&start_time));
pw = getpwuid(getuid());
memset(username,0,sizeof(username));
memset(person,0,sizeof(person));
strncpy(username,pw->pw_name,sizeof(username));
strncpy(person,pw->pw_gecos,sizeof(person));
person[sizeof(person)-1] = 0;
username[sizeof(username)-1] = 0;
session_number = random();
port_allocate(task_self(),&myMachPort);
send_sock = socket(AF_INET,SOCK_DGRAM,0);
DPSAddPort(myMachPort,(DPSPortProc)portListener,8192,0,NX_BASETHRESHOLD);
cthread_fork((cthread_fn_t)udp_listener,0);
cthread_fork((cthread_fn_t)udp_announcer,(any_t)PING_EVERYBODY);
announcementTE = DPSAddTimedEntry(ANNOUNCEMENT_FREQUENCY,
announcement_handler,0,NX_BASETHRESHOLD);
}
static void license_init()
{
/* See if we are licensed */
const char *reg;
const char *appName = [NXApp appName];
BOOL branded=YES;
char key[9];
char ks[16][8];
licensed= NO;
reg = NXGetDefaultValue(appName,REG);
if(reg){
/* If we have a default value, overwrite value stored in binary */
hex_to_binary(reg,&embedded,sizeof(embedded));
branded=NO;
}
/* Decrypt it */
asciiToKey(PRODUCT_PASSWORD,key);
desinit(0);
dessetkey(ks,key);
dedes(ks,&embedded.checksum);
/* Now check to see if string is legit */
if(checksum2(&embedded,sizeof(embedded))) return;
if(embedded.guard1[0]!=SERIAL_GUARD_1 ||
embedded.guard1[1]!=SERIAL_GUARD_2 ||
embedded.guard2[0]!=SERIAL_GUARD_1 ||
embedded.guard2[1]!=SERIAL_GUARD_2){
/* serial guard is not present */
memset(&embedded,0,sizeof(embedded));
return;
}
/* Make sure we are the right product */
if(embedded.ls.product != PRODUCT_CODE) return; /* doesn't match */
/* See if it has expired */
if(embedded.ls.flag & REG_START){
long t;
struct tm *tm;
time(&t);
tm = localtime(&t);
if( ((embedded.ls.start & 0x40) >> 4) < (tm->tm_mon+1)
|| ((embedded.ls.start & 0x04) + 1992) < (tm->tm_year+1900)){
[NXApp perform:@selector(notifyLicenseExpired:) with:nil
afterDelay:1000 cancelPrevious:NO];
memset(&embedded,0,sizeof(embedded));
return;
}
}
if(embedded.ls.flag & REG_END){
long t;
struct tm *tm;
time(&t);
tm = localtime(&t);
if( ((embedded.ls.end & 0x40) >> 4) > (tm->tm_mon+1)
|| ((embedded.ls.end & 0x04) + 1992) > (tm->tm_year+1900)){
[NXApp perform:@selector(notifyLicenseExpired:) with:nil
afterDelay:1000 cancelPrevious:NO];
memset(&embedded,0,sizeof(embedded));
return;
}
}
/* See if the file was moved and we are a network license */
if(branded){
struct stat sbuf;
if(embedded.ls.flag & REG_DEMO){
NXRunAlertPanel(0,"This is a demo version",0,0,0);
licensed = YES;
return;
}
/* Not a demo. See if it has been moved */
if(stat(NXArgv[0],&sbuf)){
NXRunAlertPanel(0,"Cannot access executable: %s",0,0,0,
NXArgv[0]);
memset(&embedded,0,sizeof(embedded));
return;
}
if(embedded.mtime+10 < sbuf.st_mtime){
/* Only do this if we are not a demo */
NXRunAlertPanel(0,
"Branded file has been moved and "
"must be re-branded.",
0,0,0);
memset(&embedded,0,sizeof(embedded));
return;
}
}
licensed = YES;
}
static void demo_expired()
{
if(licensed){
DPSRemoveTimedEntry(expiredTE); /* it got licensed */
expiredTE = 0;
return;
}
NXRunAlertPanel("Time's up!","This program is not licensed. "
"Your 2-minute demo period has expired",0,0,0);
[NXApp fatalError];
}
void registration_init2(DPSTimedEntry teNumber, double now, char *userData)
{
DPSRemoveTimedEntry(teNumber); /* remove ourselves */
network_init();
license_init();
if(licensed==NO){
[NXApp perform:@selector(notifyNotLicensed:) with:nil
afterDelay:1000 cancelPrevious:NO];
expiredTE = DPSAddTimedEntry(60 * 2.0,
demo_expired,0,NX_BASETHRESHOLD);
}
}
/* registration_init():
*
* Main entry into the registration package.
* Must be run from the main thread.
* May be run as many times as needed.
*
* Schedule the real init process with a TE
*/
void registration_init()
{
static inited=0;
if(inited) return;
inited = 1;
DPSAddTimedEntry(0.1,(DPSTimedEntryProc)registration_init2,
0,NX_BASETHRESHOLD);
}
/* bootstrap myself into the Application class */
@implementation MenuCell(Registration)
+alloc
{
registration_init();
return [super alloc];
}
+allocFromZone:(NXZone *)zone
{
registration_init();
return [super allocFromZone:zone];
}
@end
@implementation Application(Registration)
-fatalError
{
[self terminate:nil];
exit(0); /* for the bozos */
}
-noisyFatalError
{
NXRunAlertPanel("Fatal Error",
"Unable to load Registration Panels from executable. "
"Execution cannot continue.","Exit Program",0,0);
[self fatalError];
exit(0);
}
BOOL check_position(char *start,char *pos)
{
struct registration_string *test = (struct registration_string *)pos;
if(test->guard1[0]==SERIAL_GUARD_1 &&
test->guard1[1]==SERIAL_GUARD_2 &&
test->guard2[0]==SERIAL_GUARD_1 &&
test->guard2[1]==SERIAL_GUARD_2){
long offset = pos-start;
int fd;
/* open up the file and write out the new token */
fd = open(NXArgv[0],O_RDWR);
if(!fd){
NXRunAlertPanel("Branding","Could not open %s for branding",0,0,0,
NXArgv[0]);
}
lseek(fd,offset,0);
write(fd,&embedded,sizeof(embedded));
close(fd);
return YES;
}
return NO;
}
-showLicenseStatus
{
id status = NXGetNamedObject("LicenseStatusCell",licensePanel);
id user = NXGetNamedObject("LicenseNameField",licensePanel);
id exp = NXGetNamedObject("ExpField",licensePanel);
id userNum = NXGetNamedObject("UserNumberField",licensePanel);
if(!licensed){
[status setStringValue:"This program is unlicensed"];
[user setStringValue:""];
[exp setStringValue:""];
[userNum setStringValue:""];
return self;
}
if(embedded.ls.flag & REG_SINGLEUSER){
[status setStringValue:"Licensed for single-user use."];
[user setStringValue:embedded.owner];
}
if(embedded.ls.flag & REG_DEMO){
[status setStringValue:"Demonstration License"];
[user setStringValue:embedded.owner];
}
if((embedded.ls.flag & (REG_SINGLEUSER | REG_DEMO))==0){
char buf[256];
sprintf(buf,"Network License\n%d copies",lsNumUsers(&embedded.ls));
[status setStringValue:buf];
[user setStringValue:embedded.owner];
}
if(embedded.ls.flag & REG_END){
char buf[256];
sprintf(buf,"Expires %d/%d",
(embedded.ls.end & 0x40) >> 4,
(embedded.ls.end & 0x04) + 92);
[exp setStringValue:buf];
}
else{
[exp setStringValue:""];
}
[userNum setIntValue:( (embedded.ls.num[0] << 16)
+ (embedded.ls.num[1] << 8)
+ (embedded.ls.num[2] << 0))];
return self;
}
-registerProgram:sender
{
NXStream *str;
char *addr;
int len,maxlen;
long test = SERIAL_GUARD_1;
char val = *(unsigned char *)&test;
struct licenseString ls;
const char *regString;
const char *companyName;
char *aa;
unsigned char *cc = (unsigned char *)&ls;
struct stat sbuf;
char key[9];
char ks[16][8];
/* Check to see if the registration string is valid */
regString = [NXGetNamedObject("LicenseStringField",registrationPanel) stringValue];
companyName=[NXGetNamedObject("CompanyNameField",registrationPanel) stringValue];
if(!regString) [self noisyFatalError];
/* Convert to a license token */
hex_to_binary(regString,&ls,sizeof(ls));
/* Decrypt the token */
asciiToKey(PRODUCT_PASSWORD,key);
desinit(0);
dessetkey(ks,key);
dedes(ks,cc+2);
dedes(ks,cc);
if(checksum(&ls,sizeof(ls))){
NXRunAlertPanel("License","Invalid license string",0,0,0);
return nil;
}
if(strlen(companyName)==0){
NXRunAlertPanel("License","Name may not be blank",0,0,0);
return nil;
}
if(stat(NXArgv[0],&sbuf)){
NXRunAlertPanel("License","Cannot access executable: %s",0,0,0,
NXArgv[0]);
return nil;
}
/* If this is a single-user string, see if anybody else is using it */
if(ls.flag & REG_SINGLEUSER){
probes_found = 0;
memcpy(probing_num,ls.num,3);
udp_announcer(PING_EVERYBODY); /* ping everybody in this thread */
sleep(RANDOM_SLEEP_TIME+2); /* wait for results */
if(probes_found>0){
NXRunAlertPanel("License",
"That license string is being used by "
"%s (%s) on the workstation named '%s.'",
"Can't license",0,0,
probed_person,probed_username,probed_host);
return nil;
}
memset(probing_num,0,sizeof(probing_num));
}
licensed = YES;
/* Set up the embedded token */
memset(&embedded,0,sizeof(embedded));
embedded.guard1[0] = SERIAL_GUARD_1;
embedded.guard1[1] = SERIAL_GUARD_2;
embedded.guard2[0] = SERIAL_GUARD_1;
embedded.guard2[1] = SERIAL_GUARD_2;
embedded.ls = ls;
time(&embedded.ctime);
time(&embedded.mtime);
strncpy(embedded.owner,companyName,sizeof(embedded.owner)-1);
/* calc the checksum */
embedded.checksum = 0;
embedded.checksum = 65536-checksum2(&embedded,sizeof(embedded));
/* Make sure we calculated it properly */
if(checksum2(&embedded,sizeof(embedded))!=0){
NXRunAlertPanel(0,"Internal error: checksum not 0. Cannot license.",0,0,0);
return nil;
}
[registrationPanel orderOut:nil];
/* Now encrypt it before writing */
dessetkey(ks,key);
endes(ks,&embedded.checksum);
/* If this is a single-user license, write it into the defaults database */
if(ls.flag & REG_SINGLEUSER){
char buf[256];
NXWriteDefault(appName,REG,
binary_to_hex(&embedded,sizeof(embedded),buf));
NXRunAlertPanel(0,"Registered single-user license",0,0,0);
goto end;
}
NXRemoveDefault(appName,REG); /* no longer a single user */
/* Must be demo or multi-user; write to binary */
str = NXMapFile(NXArgv[0],NX_READONLY);
NXGetMemoryBuffer(str,&addr,&len,&maxlen);
for(aa=addr;aa<addr+len;aa++){
if(*aa == val
&& check_position(addr,aa)){
NXRunAlertPanel(0,"Branded successfully for %d users",
0,0,0,lsNumUsers(&embedded.ls));
goto end;
}
}
end:;
/* Now decrypt the string that's in memory */
dessetkey(ks,key);
dedes(ks,&embedded.checksum);
[self showLicenseStatus];
return self;
}
/* Display License Panel.
* If program is unlicensed, display the registration panel...
*/
-showLicensePanel:sender
{
if(!licensePanel){
[NXApp loadNibSection:"registration.nib" owner:NXApp withNames:YES];
/* wire up everything. You don't think that we're going to put this
* in the nib, do you?
*/
licensePanel = NXGetNamedObject("License Panel",NXApp);
}
if(licensePanel==nil) [NXApp noisyFatalError];
[self showLicenseStatus];
if(licensed){
[[licensePanel center] makeKeyAndOrderFront:nil]; /* display it */
}
else{
[self showRegistrationPanel:nil];
}
return self;
}
/* Display the Registration Panel */
-showRegistrationPanel:sender
{
if(!registrationPanel){
[NXApp loadNibSection:"registration.nib" owner:NXApp withNames:YES];
/* wire up everything. You don't think that we're going to put this
* in the nib, do you?
*/
registrationPanel = NXGetNamedObject("Registration Panel",NXApp);
}
if(registrationPanel==nil) [NXApp noisyFatalError];
[NXGetNamedObject("LicenseStringField",registrationPanel) setStringValue:""];
[NXGetNamedObject("CompanyNameField",registrationPanel) setStringValue:""];
[[registrationPanel center] makeKeyAndOrderFront:nil]; /* display it */
return self;
}
- notifyNotLicensed:sender
{
NXRunAlertPanel("License","This program is not licensed. "
"You will only be allowed to run it for 2 minutes.",0,0,0);
[self showRegistrationPanel:nil];
return self;
}
- notifyLicenseExpired:sender
{
NXRunAlertPanel("License","Your license has expired. "
"This program is now unlicensed",
0,0,0);
return self;
}
@end
#import "des.c"